﻿/*
 * 作者: 谢中涞,博客园:http://www.cnblogs.com/xie-zhonglai
 * 欢迎转载,但请保留出处
 * */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Data;
using System.Reflection;

namespace Liya.Crm.Core
{
    /// <summary>
    /// EF实现 数据库增删改查
    /// </summary>
    /// <typeparam name="TEntity">查询实体</typeparam>
    /// <typeparam name="TContext">DbContext 类型</typeparam>
    public class EFRepository<TEntity, TContext> : IRepository<TEntity>
        where TEntity : class
        where TContext : DbContext, new()
    {
        private TContext dbContext;

        /// <summary>
        /// 获取DbContext
        /// </summary>
        public TContext DbContext
        {
            get
            {
                if (dbContext == null)
                    dbContext = new TContext();
                return dbContext;
            }
        }

        /// <summary>
        /// 获取DbSet 
        /// </summary>
        protected DbSet<TEntity> DbSet
        {
            get
            {
                return DbContext.Set<TEntity>();
            }
        }
        /// <summary>
        /// 关联查询
        /// </summary>
        /// <typeparam name="TProperty">关联查询的属性类型</typeparam>
        /// <param name="path">关联查询属性</param>
        /// <returns></returns>
        public IQueryable<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> path)
        {
            return DbSet.Include(path);
        }

        /// <summary>
        /// 获取所有数据
        /// </summary>
        /// <returns></returns>
        public IQueryable<TEntity> Get()
        {
            return DbSet;
        }

        /// <summary>
        /// 根据指定的查询条件
        /// </summary>
        /// <param name="expression">查询条件</param>
        /// <returns></returns>
        public IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> expression)
        {
            IQueryable<TEntity> query = DbSet;
            if (expression != null)
                query = DbSet.Where(expression);
            return query;
        }

        /// <summary>
        /// 根据指定条件查询分页数据
        /// </summary>
        /// <param name="expression">查询条件</param>
        /// <param name="orderPropertyName">排序属性</param>
        /// <param name="isAscending">是否是升序查询,false为降序</param>
        /// <param name="pgIndex">分页查询起始页</param>
        /// <param name="pgSize">每页显示记录数</param>
        /// <param name="total">总记录数</param>
        /// <returns></returns>
        [Obsolete("建议使用 IQueryable<TEntity> Get(EFQueryParam<TEntity> queryParam, out int total) 方法")]
        public IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> expression,
             string orderPropertyName, bool isAscending, int pgIndex, int pgSize, out int total)
        {
            total = 0;
            IQueryable<TEntity> query = Get(expression).OrderBy(orderPropertyName, isAscending);
            //IQueryable<TEntity> query = Get(expression).OrderBy(orderPropertyName);

            //分页查询
            if (pgSize > 0)
            {
                total = query.Count();      //记录总数
                query = query.Skip(pgIndex * pgSize).Take(pgSize);
            }
            return query;
        }
        /// <summary>
        /// 根据指定条件查询数据
        /// </summary>
        /// <param name="queryParam">查询条件</param>
        /// <param name="total">符合条件的总记录数,该返回值在参数IsPagingQuery为true值时才有效</param>
        /// <returns></returns>
        public IQueryable<TEntity> Get(EFQueryParam<TEntity> queryParam, out int total)
        {
            total = 0;
            if (queryParam == null)
                return Get();

            IQueryable<TEntity> query = Get();
            if (queryParam.Expression != null)
                query = query.Where(queryParam.Expression);
            if (!string.IsNullOrEmpty(queryParam.OrderPropertyName))
                query = query.OrderBy(queryParam.OrderPropertyName, queryParam.IsAscSort);

            //分页查询
            if (queryParam.IsPagingQuery)
            {
                total = query.Count();      //记录总数
                query = query.Skip(queryParam.PageIndex * queryParam.PageSize).Take(queryParam.PageSize);
            }
            return query;
        }

        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="entity"></param>
        public void Add(TEntity entity)
        {
            DbSet.Add(entity);
        }
        /// <summary>
        /// 删除实体
        /// </summary>
        /// <param name="entity"></param>
        public void Delete(TEntity entity)
        {
            DbSet.Remove(entity);
        }

        /// <summary>
        /// 修改实体
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Update(TEntity entity)
        {
            var entry = DbContext.Entry(entity);
            if (entry != null)
            {
                if (entity != null)
                {
                    if (entry.State == EntityState.Detached)
                    {
                        //以下方法在先查询后再修改会报错,提示dbContext已经加载了一个相同主键的对象
                        DbSet.Attach(entity);
                        entry.State = EntityState.Modified;
                    }
                }
            }
        }
        /// <summary>
        /// 修改数据
        /// </summary>
        /// <param name="oldEntity">原实体</param>
        /// <param name="newEntity">修改后的实体</param>
        public void Update(TEntity dbEntity, TEntity newEntity)
        {
            var entry = DbContext.Entry(dbEntity);
            if (entry != null)
            {
                //以下方法在先查询后再修改会报错,提示dbContext已经加载了一个相同主键的对象
                //DbSet.Attach(entity);
                // entry.State = EntityState.Modified;

                //改为如下方式实现 以下代码类似于 entity=new TEntity{p1=entityToUpdate.p1,...},以下语句在当实体中有虚拟方法时候会报错
                EmitMapper.ObjectMapperManager.DefaultInstance.GetMapper<TEntity, TEntity>().Map(newEntity, dbEntity);

            }
        }

        /// <summary>
        /// 根据主键获取数据
        /// </summary>
        /// <param name="ids">主键集合</param>
        /// <returns></returns>
        public virtual TEntity Get(params object[] ids)
        {
            return DbSet.Find(ids);
        }

        /// <summary>
        /// 根据主键删除数据
        /// </summary>
        /// <param name="ids">主键集合</param>
        public virtual void Delete(params object[] ids)
        {
            var item = Get(ids);
            if (item != null)
            {
                DbContext.Entry(item).State = EntityState.Deleted;
            }
        }

        /// <summary>
        /// 提交数据修改
        /// </summary>
        public int SaveDbChange()
        {
            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (dbContext != null)
            {
                dbContext.Dispose();
                dbContext = null;
            }
        }

    }

    /// <summary>
    /// EF实现 数据库增删改查基本实现
    /// </summary>
    /// <typeparam name="TEntity">查询实体</typeparam>
    /// <typeparam name="TPkType">实体主键类型</typeparam>
    /// <typeparam name="TContext">DbContext 类型</typeparam>
    public class EFRepository<TEntity, TPkType, TContext> : EFRepository<TEntity, TContext>, IRepository<TEntity, TPkType>
        where TEntity : class, IEntity<TPkType>
        where TContext : DbContext, new()
    {
        /// <summary>
        /// 根据主键获取
        /// </summary>
        /// <param name="id">主键Id值</param>
        /// <returns></returns>
        public TEntity Get(TPkType id)
        {
            return DbSet.Find(id);
        }

        /// <summary>
        /// 根据主键删除
        /// </summary>
        /// <param name="id"></param>
        public void Delete(TPkType id)
        {
            var info = DbSet.Find(id);
            if (info != null)
            {
                DbContext.Entry(info).State = EntityState.Deleted;
            }
        }

        /// <summary>
        /// 修改,注意此方法 不支持TEntity 类中有延迟加载的属性
        /// </summary>
        /// <param name="entity"></param>
        public override void Update(TEntity entity)
        {
            var entry = DbContext.Entry(entity);
            if (entry != null)
            {
                if (entry.State == EntityState.Detached)
                {
                    //以下方法在先查询后再修改会报错,提示dbContext已经加载了一个相同主键的对象
                    //DbSet.Attach(entity);
                    // entry.State = EntityState.Modified;

                    //改为如下方式实现 以下代码类似于 entity=new TEntity{p1=entityToUpdate.p1,...},以下语句在当实体中有虚拟方法时候会报错
                    var entityToUpdate = DbSet.Find(entity.Id);
                    EmitMapper.ObjectMapperManager.DefaultInstance.GetMapper<TEntity, TEntity>().Map(entity, entityToUpdate);
                }
            }
        }


    }
}
